'''
Created on 2009-12-3

@author: wangyongtao
'''

from google.appengine.ext import db
import logging

DEFAULT_OFFSET = 50
PER_CRAWLER_COUNT = 5

class setting(db.Model):
    name = db.StringProperty(required=True)
    value = db.StringProperty(required=True)
    type = db.StringProperty(required=True, choices=set(["bool", "int", "float", "string"]))
    created = db.DateTimeProperty(auto_now_add=True)
    updated = db.DateTimeProperty(auto_now=True)
    
    @staticmethod
    def _add_setting(name, value, type, update=False):
        q = db.Query(setting).filter('name =', name)
        s = q.fetch()
        if s:
            if update:
                s.value = value
                s.type = type
            else:
                logging.error('the specified setting [%s] has already existed'%name)
                return False
        else:
            s = setting(name = name, value = value, type = type)
        s.put()
        return True
    
    @staticmethod
    def get_setting(name):
        result = None
        if not name:
            return result
        q = db.Query(setting).filter('name =', name)
        s = q.get()
        if not s:
            return result
        if s.type == 'bool':
            result = bool(s.value)
        elif s.type == 'int':
            result = int(s.value)
        elif s.type == 'float':
            result = float(s.value)
        else:
            result = s.value
        return result
            
    @staticmethod
    def add_setting_bool(name, value):
        bVal = bool(value)
        type = 'bool'
        setting._add_setting(name, str(bVal), type)
    
    @staticmethod
    def add_setting_int(name, value):
        intVal = int(value)
        type = 'int'
        setting._add_setting(name, intVal, type)
        
    @staticmethod
    def add_setting_float(name, value):
        floatVal = float(value)
        type = 'float'
        setting._add_setting(name, floatVal, type)
    
    @staticmethod
    def add_setting_string(name, value):
        strVal = str(value)
        type = 'string'
        setting._add_setting(name, strVal, type)
        
class host(db.Model):
    name = db.StringProperty(required=True)
    cbnid = db.IntegerProperty(required = True)
    title_cn = db.StringProperty(required = True)
    title_en = db.StringProperty()
    avatar = db.URLProperty()
    description = db.TextProperty()
    urlcbn = db.URLProperty(required = True) #url for crawler
    detail = db.URLProperty()
    created = db.DateTimeProperty(auto_now_add=True)
    updated = db.DateTimeProperty(auto_now=True)
    
    @staticmethod
    def add_host(name, id, title_cn, urlcbn):
        q = db.Query(host).filter('cbnid =', int(id))
        c = q.get()
        if not c:
            c = host(name = name, cbnid = int(id), title_cn = title_cn, urlcbn = db.Link(urlcbn))
            c.put()
        return c
    
class cbncolumn(db.Model):
    name = db.StringProperty(required = True)
    cbnid = db.IntegerProperty(required = True)
    title_cn = db.StringProperty(required = True)
    title_en = db.StringProperty()
    description = db.TextProperty()
    urlcbn = db.URLProperty(required = True) #url for crawler
    cover = db.URLProperty()
    host = db.StringProperty()  #reference with host.name
    radio = db.BooleanProperty(default=False)
    schedule = db.StringListProperty()
    created = db.DateTimeProperty(auto_now_add=True)
    updated = db.DateTimeProperty(auto_now=True)
    
    @staticmethod
    def get_column_cbnid(cbnid):
        return db.Query(cbncolumn).filter('cbnid =', int(cbnid)).get()
        
    @staticmethod
    def get_all_columns():
        q = db.Query(cbncolumn).order('created')
        return q.fetch(1000)
    
    @staticmethod
    def get_radio_columns():
        q = db.Query(cbncolumn).filter('radio =', True)
        return q.fetch(100)
        
    @staticmethod
    def add_cbncolumn(name, id, title_cn, urlcbn):
        q = db.Query(cbncolumn).filter('cbnid =', int(id))
        c = q.get()
        if not c:
            c = cbncolumn(name = name, cbnid = int(id), title_cn = title_cn, urlcbn = db.Link(urlcbn))
            c.put()
        return c
            
    def delete_radio_shows(self):
        if self.radio:
            shows = self.get_shows(500)
            db.delete(shows)
            
    def get_count_show(self):
        return db.Query(cbnshow).filter('cbncolumnid =', self.cbnid).count()
    
    def get_count_downloaded_show(self):
        return db.Query(cbnshow).filter('cbncolumnid =', self.cbnid).filter('downloaded =', True).count()
    
    def get_shows(self, limit = 1000, offset = 0):
        return db.Query(cbnshow).filter('cbncolumnid =', self.cbnid).order('-showtime').fetch(limit, offset)
    
class cbnshow(db.Model):
    column = db.StringProperty()
    cbnid = db.IntegerProperty(required = True)
    cbncolumnid = db.IntegerProperty(required = True)
    title_cn = db.StringProperty(required = True)
    title_en = db.StringProperty()
    description = db.TextProperty()
    urlcbn = db.URLProperty() #url for crawler
    urlcbnvideo = db.URLProperty() #original video address
    urlshowvideo = db.URLProperty()
    downloaded = db.BooleanProperty(default=False)
    showtime = db.DateProperty()
    host = db.StringListProperty()
    created = db.DateTimeProperty(auto_now_add=True)
    updated = db.DateTimeProperty(auto_now=True)
    
    @staticmethod
    def add_cbnshow(cbnid, columnid, title, column, htmlurl, mmsurl):
        q = db.Query(cbnshow).filter('cbncolumnid =', int(columnid)).filter('cbnid =', int(cbnid))
        s = q.get()
        if not s:
            s = cbnshow(column = column, cbnid = int(cbnid), cbncolumnid = int(columnid), title_cn = title, urlcbn = db.Link(htmlurl), urlcbnvideo = db.Link(mmsurl))
            s.put()
        return s
    
    @staticmethod
    def show_exist(cbnid, columnid):
        q = db.Query(cbnshow).filter('cbncolumnid =', int(columnid)).filter('cbnid =', int(cbnid))
        return q.get()
    
    @staticmethod
    def get_show_count():
        return db.Query(cbnshow).count()
    
    @staticmethod
    def get_downloaded_show_count():
        return db.Query(cbnshow).filter('downloaded =', True).count()
        
    @staticmethod
    def get_downloaded_shows(limit, offset):
        if not limit:
            limit = 100
        if not offset:
            offset = 0
        return db.Query(cbnshow).filter('downloaded =', True).order('-showtime').fetch(limit, offset)

class sodexounit(db.Model):
    url = db.LinkProperty(required=True)
    unitid = db.IntegerProperty(required=True)
    title = db.StringProperty()
    address = db.StringProperty()
    telephone = db.StringProperty()
    location = db.GeoPtProperty()
    desc = db.TextProperty()
    tags = db.StringListProperty()
    created = db.DateTimeProperty(auto_now_add=True)
    updated = db.DateTimeProperty(auto_now=True) 
    
    @staticmethod
    def get_unit_by_id(unitid):
        return db.Query(sodexounit).filter('unitid =', int(unitid)).get()
    
    @staticmethod
    def add_unit(unitid, url, title=None, address=None, telephone=None):
        u = db.Query(sodexounit).filter('unitid =', int(unitid)).get()
        if not u:
            u = sodexounit(url = url, unitid = int(unitid))
        if title:
            u.title = title
        if address:
            u.address = address
        if telephone:
            u.telephone = telephone
        u.put()
        return u

class log(db.Model):
    action = db.StringProperty()
    content = db.StringProperty()
    target = db.StringProperty()
    successed = db.BooleanProperty()
    created = db.DateTimeProperty(auto_now_add=True)
    updated = db.DateTimeProperty(auto_now=True) 
    
    _ACTION_CRON = 'cron'
    _ACTION_DOWNLOAD = 'download'
    _ACTION_VERIFY = 'verify'
    _ACTION_SIZE = 'size'
    _ACTION_SHOWPAGE = 'showpage'
    _ACTION_SHOW_DOWNLOAD = 'showdownload'
    _ACTION_UPDATE = 'update'
    
    @staticmethod  
    def get_non_crawled_shows(count):
        if not count:
            count = PER_CRAWLER_COUNT
        q = db.Query(log).filter('action =', log._ACTION_CRON).filter('target =', 'show').filter('successed =', True).order('-created')
        a = q.get()
        if a:
            s = db.Query(cbncolumn).filter('urlcbn =', a.content).get()
            return db.Query(cbncolumn).filter('created >', s.created).filter('radio =', False).order('created').fetch(count)
        else:
            return db.Query(cbncolumn).filter('radio =', False).order('created').fetch(count)
    
    @staticmethod
    def add_crawler_show(url, successed):
        l = log(action=log._ACTION_CRON, content = url, target='show', successed=successed)
        l.put()
    
    @staticmethod
    def get_showpage(column):
        q = db.Query(log).filter('action =', log._ACTION_SHOWPAGE).filter('target =', str(column.cbnid)).filter('successed =', True).order('-created')
        a = q.get()
        if a:
            return int(a.content)+1
        else:
            return 1
        
    @staticmethod
    def get_non_def_shows(count):
        return db.Query(cbnshow).filter('downloaded =', True).order('created').fetch(1000)
    
    @staticmethod
    def add_def_show(show, successed):
        l = log(action=log._ACTION_UPDATE, content = show.urlcbnvideo, target='show', successed=successed)
        l.put()
    
    @staticmethod
    def update_show_download(showkey, successed):
        if not showkey: return
        show = cbnshow.get(showkey)
        if show:
            logging.debug('downloaded show:%s'%show.urlcbnvideo)
            show.downloaded = bool(successed)
            show.put()
            l = log(action=log._ACTION_DOWNLOAD, target='show', content=show.urlcbnvideo, successed=bool(successed))
            l.put()
    
    @staticmethod
    def get_non_downloaded_shows(limit, offset):
        if not limit:
            limit = PER_CRAWLER_COUNT
        if not offset:
            offset = 0
        return db.Query(cbnshow).filter('downloaded =', False).order('-showtime').fetch(limit, offset)
    
    @staticmethod
    def get_non_downloaded_shows_by_colid(limit, offset, colid):
        if not limit:
            limit = PER_CRAWLER_COUNT
        if not offset:
            offset = 0
        return db.Query(cbnshow).filter('cbncolumnid =', int(colid)).filter('downloaded =', False).order('-showtime').fetch(limit, offset)
    
    @staticmethod
    def add_showpage(column, page, successed):
        l = log(action = log._ACTION_SHOWPAGE, content = str(page), target=str(column.cbnid), successed=successed)
        l.put()
        
    @staticmethod  
    def get_non_crawled_hosts(count):
        if not count:
            count = PER_CRAWLER_COUNT
        q = db.Query(log).filter('action =', log._ACTION_CRON).filter('target =', 'host').filter('successed =', True).order('-created')
        a = q.get()
        if a:
            s = db.Query(host).filter('urlcbn =', a.content).get()
            return db.Query(host).filter('created >', s.created).order('created').fetch(count)
        else:
            return db.Query(host).order('created').fetch(count)
    
    @staticmethod
    def add_crawler_host(url, successed):
        l = log(action=log._ACTION_CRON, content = url, target='host', successed=successed)
        l.put()
        
        